home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 12 / BBS in a box XII-2.iso / Files II / Prog / T / TransEdit 3.05.sit / TransEdit v 3.05 / TransEdit.c / TransEdit.c
Encoding:
C/C++ Source or Header  |  1994-02-23  |  43.0 KB  |  1,851 lines  |  [TEXT/KAHL]

  1. /*
  2.  * - Check that SaveFile puts correct initial name in.
  3.  * - Make sure that filename gets initialized properly in all cases.
  4.  */
  5.  
  6. /*
  7.  * TransEdit.c version 3.05 - TransSkel plug-in module supporting an
  8.  * arbitrary number of generic edit windows.  Each window may be
  9.  * bound to a file.
  10.  * 
  11.  * *** Requires FakeAlert.c for proper linking! ***
  12.  *
  13.  * TransSkel and TransEdit are public domain.  For more information,
  14.  * contact:
  15.  *
  16.  *             Paul DuBois
  17.  *             Wisconsin Regional Primate Research Center
  18.  *             1220 Capitol Court
  19.  *             Madison, WI  53715-1299  USA
  20.  *
  21.  * Internet:    dubois@primate.wisc.edu
  22.  *        
  23.  *
  24.  * This version of TransEdit is written for THINK C 6.0.
  25.  * THINK C is a trademark of:
  26.  *
  27.  *             Symantec Corporation
  28.  *             10201 Torre Avenue
  29.  *             Cupertino, CA 95014  USA
  30.  *
  31.  * History
  32.  * 08/25/86    Genesis.  Beta version.
  33.  * 09/15/86
  34.  * - Changed to allow arbitrary number of windows.
  35.  *
  36.  * 11/04/86 Release 1.0
  37.  * - Added conditional stuff to allow compilation in single- or
  38.  * multiple-window mode.
  39.  *
  40.  * 01/17/87 Release 1.01
  41.  * - The window type when a new window is created is documentProc+8
  42.  * now, so that the window will have a zoom box on a machine with
  43.  * 128K ROMS.  Default file name in SaveFile() is initialized after
  44.  * SyncAllGlobals() - fixing bug found by Owen Hartnett.
  45.  *
  46.  * 01/29/89 Release 2.0
  47.  * - Converted to work with TransSkel 2.0.  2-byte and
  48.  * 4-byte integer types are typedef'ed to Integer and LongInt to
  49.  * ease porting.  Added SystemEdit() check to Edit menu handler.
  50.  *
  51.  * 16 Jun 92 Release 3.01
  52.  * - Ported for TransSkel 3.00. Basically this amounted to adding
  53.  * function prototypes.
  54.  * - Use real curly quotes in FakeAlert() messages.
  55.  *
  56.  * 05 Jun 93 Release 3.02
  57.  * - Conversion for THINK C 6.0.
  58.  *
  59.  * 08 Jun 93 Release 3.03
  60.  * - Took out all the stuff to allow compiling to handle only a single
  61.  * edit window.  The savings in bytes of object code is no longer worth
  62.  * the extra source code complexity.  It's also unnecessary because I no longer
  63.  * maintain a window list, since I ...
  64.  * - Reimplemented linked list holding edit window data using TransSkel's
  65.  * window property functions (new in TS 3.00).  The property type is
  66.  * skelWPropEditWind, and the data value is a handle to the window data
  67.  * structure.
  68.  * - Took out all the "register" declarations.  The compiler's smart enough
  69.  * now that they don't make any difference, so they're just clutter.
  70.  * 05 Jul 93
  71.  * - Redid FakeAlert() calls to correspond better to new button positioning.
  72.  * (See FakeAlert.c remarks.)
  73.  * - Fixed Activate() and Update() so that when a window goes inactive,
  74.  * the scroll bar is hidden (and only the frame drawn).  Previously, the
  75.  * scroll bar was just made inactive, which doesn't quite conform to the user
  76.  * interface guidelines.
  77.  * 18 Dec 93
  78.  * - Untitled windows have "untitled" rather than "Untitled" in title bar,
  79.  * and first untitled window isn't given a number, in accordance with Apple
  80.  * guidelines.
  81.  * - Replaced SFGetFile()/SFPutFile() with SFPGetFil()/SFPPutFile() so can
  82.  * use SkelDlogFilter().
  83.  * 21 Dec 93
  84.  * - Use color grafports when available.
  85.  * 04 Jan 94
  86.  * - Undid Integer/LongInt type stuff back to short/long.
  87.  *
  88.  * 18 Jan 94 Release 3.04
  89.  * - FakeAlert() code revamped.  See FakeAlert.c.
  90.  * 19 Jan 94
  91.  * - Window creation routines do better checks for allocation failures.
  92.  * - Much rewriting to eliminate many global variables.  No more SyncGlobals().
  93.  * 20 Jan 94
  94.  * - Fixed bug with missing SkelRmveDlogFilter() after SFPGetFile() call.
  95.  * 23 Jan 94
  96.  * - Fixed bug in EWindowEditOp(); edit window doesn't need to be marked dirty
  97.  * for Copy operation.
  98.  *
  99.  * 21 Feb 94 Release 3.05
  100.  * - Updated for TransSkel 3.11.
  101.  * - Converted interface to be Pascal-compatible.
  102.  */
  103.  
  104. # include    "TransSkel.h"
  105.  
  106. # include    "TransEdit.h"
  107.  
  108.  
  109. # define    normalHilite    0
  110. # define    dimHilite        255
  111.  
  112. /*
  113.  * Maximum size of file we'll attempt to read.  The value leaves
  114.  * a little cushion against the TextEdit limit of 32767.
  115.  */
  116.  
  117. # define    maxFileSize        32000
  118.  
  119.  
  120. # define    WindowIsActive(w)    ((WindowPeek) (w))->hilited
  121.  
  122.  
  123. /*
  124.  * New(TypeName) returns handle to new object, for any TypeName.
  125.  * If there is insufficient memory, the result is nil.
  126.  */
  127.  
  128. # define    New(type)    (type **) NewHandle ((Size) sizeof (type))
  129.  
  130.  
  131. # define    enter            3
  132. # define    cr                13
  133.  
  134.  
  135. typedef enum            /* Edit menu item numbers */
  136. {
  137.     undo = 1,
  138.     /* --- */
  139.     cut = 3,
  140.     copy,
  141.     paste,
  142.     clear        /* (it's ok if the host doesn't have this item) */
  143. };
  144.  
  145.  
  146. /*
  147.  * Default values for edit window text display characteristics
  148.  * and event notification procedures.  Used when new edit windows
  149.  * are created.
  150.  */
  151.  
  152. static short    e_font = monaco;                /* default font                 */
  153. static short    e_size = 9;                        /* default pointsize            */
  154. static short    e_wrap = 0;                        /* default word wrap (on)       */
  155. static short    e_just = teJustLeft;            /* default justification        */
  156. static TEditKeyProcPtr        e_key = nil;        /* default key procedure        */
  157. static TEditActivateProcPtr    e_activate = nil;    /* default activation procedure */
  158. static TEditCloseProcPtr    e_close = nil;        /* default close procedure      */
  159.  
  160.  
  161. /*
  162.  * Edit window document record.  A handle to a window's document
  163.  * record is stored in the window's property list.
  164.  */
  165.  
  166. typedef struct DocRecord    DocRecord, *DocPtr, **DocHandle;
  167.  
  168. struct DocRecord
  169. {
  170.     WindowPtr        editWind;    /* the edit window                   */
  171.     Boolean            bound;        /* whether window is bound to file   */
  172.     SFReply            editFile;    /* file it's bound to, if bound true */
  173.     TEHandle        editTE;        /* window text                       */
  174.     Boolean            dirty;        /* whether text modified since save  */
  175.     ControlHandle    eScroll;    /* scroll bar                        */
  176.     short            visLines;    /* # lines visible in window, max    */
  177.     TEditKeyProcPtr            eKey;        /* key click notifier        */
  178.     TEditActivateProcPtr    eActivate;    /* activate event notifier   */
  179.     TEditCloseProcPtr        eClose;        /* close notifier            */
  180. };
  181.  
  182.  
  183. /*
  184.  * Macros for accessing parts of a document record, given a document
  185.  * handle.
  186.  */
  187.  
  188. # define    DocWind(doc)            ((**doc).editWind)
  189. # define    DocBound(doc)            ((**doc).bound)
  190. # define    DocFile(doc)            ((**doc).editFile)
  191. # define    DocTE(doc)                ((**doc).editTE)
  192. # define    DocDirty(doc)            ((**doc).dirty)
  193. # define    DocScroll(doc)            ((**doc).eScroll)
  194. # define    DocVisLines(doc)        ((**doc).visLines)
  195. # define    DocKeyProc(doc)            ((**doc).eKey)
  196. # define    DocActivateProc(doc)    ((**doc).eActivate)
  197. # define    DocCloseProc(doc)        ((**doc).eClose)
  198.  
  199.  
  200. static Point    dlogWhere = { 70, 100 };    /* GetFile/PutFile location */
  201. static OSType    creator = 'TEDT';            /* default file creator */
  202.  
  203. static RgnHandle    clipRgn = (RgnHandle) nil;
  204.  
  205.  
  206. /* -------------------------------------------------------------------- */
  207. /*                Miscellaneous Internal (private) Routines                */
  208. /* -------------------------------------------------------------------- */
  209.  
  210.  
  211. static void
  212. ErrMesg (StringPtr s)
  213. {
  214.     (void) FakeAlert (s, "\p", "\p", "\p", 1, 1, 0, "\pOK", "\p", "\p");
  215. }
  216.  
  217.  
  218. /*
  219.  * Save and restore the current window's clip region
  220.  */
  221.  
  222. static void
  223. SaveClipRgn (void)
  224. {
  225.     clipRgn = NewRgn ();
  226.     GetClip (clipRgn);
  227. }
  228.  
  229.  
  230. static void
  231. RestoreClipRgn (void)
  232. {
  233.     SetClip (clipRgn);
  234.     DisposeRgn (clipRgn);
  235. }
  236.  
  237.  
  238. /*
  239.  * Draw grow box in lower right hand corner of window.
  240.  */
  241.  
  242. static void
  243. DrawGrowBox (WindowPtr w)
  244. {
  245. Rect    r;
  246.  
  247.     SaveClipRgn ();
  248.     r = w->portRect;
  249.     r.left = r.right - 15;        /* draw only in corner */
  250.     r.top = r.bottom - 15;
  251.     ClipRect (&r);
  252.     DrawGrowIcon (w);
  253.     RestoreClipRgn ();
  254. }
  255.  
  256.  
  257. /*
  258.  * Generate title for new untitled document.  First one is
  259.  * unnumbered, others are numbered.
  260.  */
  261.  
  262. static void
  263. Untitled (StringPtr    name)
  264. {
  265. static long    num = 0;
  266. Str255        numBuf;
  267.  
  268.     BlockMove ("\puntitled", name, 9L);
  269.     if (++num > 1)
  270.     {
  271.         name[++name[0]] = ' ';
  272.         NumToString (num, numBuf);
  273.         BlockMove (&numBuf[1], &name[name[0]+1], (long) numBuf[0]);
  274.         name[0] += numBuf[0];
  275.     }
  276. }
  277.  
  278.  
  279. /* -------------------------------------------------------------------- */
  280. /*            Lowest-level Internal (Private) Edit Window Routines        */
  281. /* -------------------------------------------------------------------- */
  282.  
  283.  
  284. /*
  285.  * Get document handle associated with edit window.
  286.  * Return nil if window isn't a known edit window.
  287.  */
  288.  
  289. static DocHandle
  290. WindDocHandle (WindowPtr w)
  291. {
  292. SkelWindPropHandle    ph;
  293. DocHandle    doc = (DocHandle) nil;
  294.  
  295.     if (w != (WindowPtr) nil)
  296.     {
  297.         ph = SkelGetWindProp (w, skelWPropEditWind);
  298.         if (ph != (SkelWindPropHandle) nil)
  299.             doc = (DocHandle) (**ph).skelWPropData;
  300.     }
  301.     return (doc);
  302. }
  303.  
  304.  
  305. /* -------------------------------------------------------------------- */
  306. /*                    Internal (private) Display Routines                    */
  307. /* -------------------------------------------------------------------- */
  308.  
  309.  
  310. /*
  311.  * Calculate the dimensions of the editing rectangle for a window
  312.  * (which is assumed to be the current port).  (The viewRect and
  313.  * destRect are the same size.)  Assumes the port, text font and
  314.  * text size are all set properly.  The viewRect is sized so that
  315.  * an integral number of lines can be displayed in it, i.e., so that
  316.  * a partial line never shows at the bottom.  If that's not done,
  317.  * funny things can happen to the caret.
  318.  */
  319.  
  320. static void
  321. CalcEditRect (WindowPtr w, Rect *r)
  322. {
  323. FontInfo    f;
  324. short        lineHeight;
  325.  
  326.     GetFontInfo (&f);
  327.     lineHeight = f.ascent + f.descent + f.leading;
  328.     *r = w->portRect;
  329.     r->left += 4;
  330.     r->right -= 17;            /* leave room for scroll bar + 2 */
  331.     r->top += 2;
  332.     r->bottom = r->top + ((r->bottom - r->top - 2) / lineHeight) * lineHeight;
  333. }
  334.  
  335.  
  336. /*
  337.  * Calculate the dimensions of the scroll bar rectangle for a
  338.  * window.  Make sure that the edges overlap the window frame and
  339.  * the grow box.
  340.  */
  341.  
  342. static void
  343. CalcScrollRect (WindowPtr w, Rect *r)
  344. {
  345.     *r = w->portRect;
  346.     ++r->right;
  347.     --r->top;
  348.     r->left = r->right - 16;
  349.     r->bottom -= 14;
  350. }
  351.  
  352.  
  353. /*
  354.  * Return true if the mouse is in the non-scrollbar part of an
  355.  * edit window.
  356.  */
  357.  
  358. static Boolean
  359. PtInText (WindowPtr w, Point pt)
  360. {
  361. Rect    r;
  362.  
  363.     r = w->portRect;
  364.     r.right -= 15;
  365.     return (PtInRect (pt, &r));
  366. }
  367.  
  368.  
  369. /*
  370.  * Set the cursor appropriately.  If theCursor == iBeamCursor, check
  371.  * that it's really in the text area of an edit window (and if not
  372.  * set the cursor to an arrow instead).  Otherwise, set the cursor
  373.  * to the given type (usually a watch).
  374.  *
  375.  * Pass -1 for theCursor to set the cursor to the arrow.
  376.  */
  377.  
  378. static void
  379. FixCursor (short theCursor)
  380. {
  381. WindowPtr    w;
  382. GrafPtr        savePort;
  383. Point        pt;
  384.  
  385.     if (theCursor == iBeamCursor)            /* check whether there's an edit */
  386.     {                                        /* window in front and if so,    */
  387.         theCursor = -1;                        /* whether the cursor's in its   */
  388.         w = FrontWindow ();                    /* text area                     */
  389.         if (IsEWindow (w))
  390.         {
  391.             GetPort (&savePort);
  392.             SetPort (w);
  393.             GetMouse (&pt);
  394.             if (PtInText (w, pt))
  395.                 theCursor = iBeamCursor;
  396.             SetPort (savePort);
  397.         }
  398.     }
  399.     SetCursor (theCursor == -1 ? &arrow : *GetCursor (theCursor));
  400. }
  401.  
  402.  
  403. /*
  404.  * Calculate the number of lines currently scrolled off
  405.  * the top of an edit record.
  406.  */
  407.  
  408. static short
  409. LinesOffTop (TEHandle hTE)
  410. {
  411.     return (((**hTE).viewRect.top - (**hTE).destRect.top) / (**hTE).lineHeight);
  412. }
  413.  
  414.  
  415. /*
  416.  * Return the line number that the caret (or the beginning of
  417.  * the currently selected text) is in.  Value returned is in
  418.  * the range 0..(**editTE).nLines.  If = (**editTE).nLines, the
  419.  * caret is past the last line.  The only special case to watch out
  420.  * for is when the caret is at the very end of the text.  If the
  421.  * last character is not a carriage return, then the caret is on
  422.  * the (nLines-1)th line, not the (nLines)th line.
  423.  *
  424.  * (This really should do a binary search for speed.)
  425.  */
  426.  
  427. static short
  428. LineWithCaret (TEHandle hTE)
  429. {
  430. short    i;
  431. short    nLines;
  432. short    teLength;
  433. short    selStart;
  434. short    lineStart;
  435.  
  436.     selStart = (**hTE).selStart;
  437.     nLines = (**hTE).nLines;
  438.     teLength = (**hTE).teLength;
  439.  
  440.     if (selStart == teLength)
  441.     {
  442.         if (teLength != 0 && (*((**hTE).hText))[teLength-1] != cr)
  443.             return (nLines - 1);
  444.         return (nLines);
  445.     }
  446.  
  447.     for (i = 0; /* empty */; ++i)
  448.     {
  449.         if ((lineStart = (**hTE).lineStarts[i]) >= selStart)
  450.         {
  451.             if (lineStart != selStart)
  452.                 --i;
  453.             return (i);
  454.         }
  455.     }
  456. }
  457.  
  458.  
  459. /*
  460.  * Return the number of the last displayable line.  That's one
  461.  * more than nLines if the text is empty or the last character
  462.  * is a carriage return.
  463.  */
  464.  
  465. static short
  466. LastLine (TEHandle hTE)
  467. {
  468. short    nLines;
  469. short    teLength;
  470.  
  471.     nLines = (**hTE).nLines;
  472.     teLength = (**hTE).teLength;
  473.  
  474.     if (teLength == 0 || (*((**hTE).hText))[teLength-1] == cr)
  475.         nLines++;
  476.     return (nLines);
  477. }
  478.  
  479.  
  480. /*
  481.  * Highlight the scroll bar properly.  This means that it's not
  482.  * made active if the window itself isn't active, even if
  483.  * there's enough text to fill the window.
  484.  */
  485.  
  486. static void
  487. HiliteScroll (DocHandle doc)
  488. {
  489. WindowPtr    w;
  490. ControlHandle    scroll;
  491. short    hilite;
  492.  
  493.     w = DocWind (doc);
  494.     scroll = DocScroll (doc);
  495.     hilite = (WindowIsActive (w) && GetCtlMax (scroll) > 0
  496.                 ? normalHilite : dimHilite);
  497.     HiliteControl (scroll, hilite);
  498. }
  499.  
  500.  
  501. /*
  502.  * Set scroll bar current value (but only if it's different than
  503.  * the current value, to avoid needless flashing).
  504.  */
  505.  
  506. static void
  507. SetScrollValue (ControlHandle scroll, short value)
  508. {
  509.     if (GetCtlValue (scroll) != value)
  510.         SetCtlValue (scroll, value);
  511. }
  512.  
  513.  
  514. /*
  515.  * Set the maximum value of the scroll bar.  It's set so that if
  516.  * there's more text than fits in the window, the bottom line can
  517.  * be scrolled up at least a little below the bottom of the window.
  518.  *
  519.  * The shenanigans with topLines and scrollableLines have to do with
  520.  * the case where there may be less text than fills the window, but
  521.  * most of it's scrolled off the top.  This can happen when you
  522.  * scroll a bunch of stuff up, then delete everything visible in
  523.  * the window.
  524.  */
  525.  
  526. static void
  527. SetScrollMax (DocHandle doc)
  528. {
  529. ControlHandle    scroll;
  530. TEHandle        hTE;
  531. short    topLines;
  532. short    scrollableLines;
  533. short    max;
  534.  
  535.     scroll = DocScroll (doc);
  536.     hTE = DocTE (doc);
  537.  
  538.     topLines = LinesOffTop (hTE);
  539.     scrollableLines = LastLine (hTE) - DocVisLines (doc);;
  540.     max = (topLines > scrollableLines ? topLines : scrollableLines);
  541.  
  542.     if (max < 0)
  543.         max = 0;
  544.  
  545.     if (max != GetCtlMax (scroll))
  546.     {
  547.         SetCtlMax (scroll, max);
  548.         HiliteScroll (doc);
  549.     }
  550. }
  551.  
  552.  
  553. /*
  554.  * Scroll to the correct position.  lDelta is the
  555.  * amount to CHANGE the current scroll setting by.
  556.  * Positive scrolls the text up, negative down.
  557.  */
  558.  
  559. static void
  560. ScrollText (DocHandle doc, short lDelta)
  561. {
  562. ControlHandle    scroll;
  563. TEHandle    hTE;
  564. short    topVisLine;
  565. short    newTopVisLine;
  566.  
  567.     scroll = DocScroll (doc);
  568.     hTE = DocTE (doc);
  569.  
  570.     topVisLine = LinesOffTop (hTE);
  571.     newTopVisLine = topVisLine + lDelta;
  572.     if (newTopVisLine < 0)                    /* clip to range */
  573.         newTopVisLine = 0;
  574.     if (newTopVisLine > GetCtlMax (scroll))
  575.         newTopVisLine = GetCtlMax (scroll);
  576.     SetScrollValue (scroll, newTopVisLine);
  577.     TEScroll (0, (topVisLine - newTopVisLine ) * (**hTE).lineHeight, hTE);
  578. }
  579.  
  580.  
  581. /*
  582.  * Scroll to home position without redrawing.
  583.  */
  584.  
  585. static void
  586. ScrollToHome (TEHandle hTE)
  587. {
  588. Rect    r;
  589.  
  590.     r = (**hTE).destRect;
  591.     OffsetRect (&r, 0, 2 - r.top);
  592.     (**hTE).destRect = r;
  593. }
  594.  
  595.  
  596. /*
  597.  * ClikLoop proc for autoscrolling text when the mouse is dragged out
  598.  * of the text view rectangle.
  599.  *
  600.  * The clipping region has to be set to include the scroll bar,
  601.  * because whenever this proc is called, TextEdit has the region set
  602.  * to the view rectangle - if it's not reset, changes to the scroll
  603.  * bar won't show up!
  604.  *
  605.  * AutoScroll() uses scrollDoc, which must be set in Mouse() before
  606.  * calling TEClick(), which calls AutoScroll().
  607.  */
  608.  
  609.  
  610. static DocHandle    scrollDoc;
  611. static short        scrollPart;
  612.  
  613. static pascal Boolean
  614. AutoScroll (void)
  615. {
  616. WindowPtr    w;
  617. TEHandle    hTE;
  618. Point    p;
  619. Rect    r;
  620.  
  621.     w = DocWind (scrollDoc);
  622.     hTE = DocTE (scrollDoc);
  623.  
  624.     SaveClipRgn ();
  625.     ClipRect (&w->portRect);
  626.     GetMouse (&p);
  627.     r = (**hTE).viewRect;
  628.     if (p.v < r.top)
  629.         ScrollText (scrollDoc, -1);    /* back one line */
  630.     else if (p.v > r.bottom)
  631.         ScrollText (scrollDoc, 1);    /* forward one line */
  632.     RestoreClipRgn ();
  633.     return (true);            /* true = 'keep tracking mouse' */
  634. }
  635.  
  636.  
  637. /*
  638.  * Filter proc for tracking mousedown in scroll bar.
  639.  *
  640.  * I suspect odd scrolling may occur for hits in paging regions if
  641.  * the window is allowed to size such that less than two lines show.
  642.  *
  643.  * TrackScroll() uses scrollDoc and scrollPart, which must be set in
  644.  * Mouse() before calling TrackControl(), which calls TrackScroll().
  645.  * scrollPart is the original part code in which the mousedown occurred.
  646.  */
  647.  
  648. static pascal void
  649. TrackScroll (ControlHandle theScroll, short partCode)
  650. {
  651. short    lDelta;
  652. short    visLines = DocVisLines (scrollDoc);
  653.  
  654.     if (partCode == scrollPart)    /* still in same part? */
  655.     {
  656.         switch (partCode)
  657.         {
  658.             case inUpButton: lDelta = -1; break;
  659.             case inDownButton: lDelta = 1; break;
  660.             case inPageUp: lDelta = -(visLines - 1); break;
  661.             case inPageDown: lDelta = visLines - 1; break;
  662.         }
  663.         ScrollText (scrollDoc, lDelta);
  664.     }
  665. }
  666.  
  667.  
  668. /*
  669.  * Set the scroll bar properly and adjust the text in the
  670.  * window so that the line containing the caret is visible.
  671.  * If the line with the caret is more than a line outside of
  672.  * the viewRect, try to place it in the middle of the window.
  673.  *
  674.  * Yes, it is necessary to call SetScrollMax() at the end.
  675.  */
  676.  
  677. static void
  678. AdjustDisplay (DocHandle doc)
  679. {
  680. ControlHandle    scroll;
  681. TEHandle    hTE;
  682. short        visLines;
  683. short    caretLine;
  684. short    topVisLine;
  685. short    d;
  686.  
  687.     scroll = DocScroll (doc);
  688.     hTE = DocTE (doc);
  689.     visLines = DocVisLines (doc);
  690.  
  691.     SetScrollMax (doc);
  692.     caretLine = LineWithCaret (hTE);
  693.     topVisLine = LinesOffTop (hTE);
  694.     if ((d = caretLine - topVisLine) < 0)
  695.         ScrollText (doc, d == -1 ? -1 : d - visLines / 2);
  696.     else if (( d = caretLine - (topVisLine + visLines - 1)) > 0)
  697.         ScrollText (doc, d == 1 ? 1 : d + visLines / 2);
  698.     else
  699.         SetScrollValue (scroll, topVisLine);
  700.     SetScrollMax (doc);    /* might have changed from scrolling */
  701. }
  702.  
  703.  
  704. /*
  705.  * Overhaul the entire display.  This is called after catastrophic
  706.  * events, such as resizing the window, or changes to the word
  707.  * wrap style.  It makes sure the view and destination rectangles
  708.  * are sized properly, and that the bottom line of text never
  709.  * scrolls up past the bottom line of the window (if there's
  710.  * enough to fill the window), and that the scroll bar max and
  711.  * current values are set properly.
  712.  *
  713.  * Resizing the dest rect just means resetting the right edge
  714.  * (the top is NOT reset), since text might be scrolled off the
  715.  * top (i.e., destRect.top != 0).
  716.  *
  717.  * Doesn't redraw the control, though!
  718.  */
  719.  
  720. static void
  721. OverhaulDisplay (DocHandle doc, Boolean showCaret, Boolean recalc)
  722. {
  723. TEHandle    hTE;
  724. Rect        r;
  725.  
  726.     hTE = DocTE (doc);
  727.     r = (**hTE).viewRect;        /* erase current viewRect */
  728.     EraseRect (&r);
  729.     CalcEditRect (DocWind (doc), &r);        /* calculate new viewRect */
  730.     (**hTE).destRect.right = r.right;
  731.     (**hTE).viewRect = r;
  732.     if (recalc)
  733.         TECalText (hTE);            /* recalculate line starts */
  734.     DocVisLines (doc) = (r.bottom - r.top) / (**hTE).lineHeight;
  735.  
  736.     /*
  737.      * If there is text, but none of it is visible in the window
  738.      * (it's all scrolled off the top), pull some down.
  739.      */
  740.  
  741.     if (showCaret)
  742.         AdjustDisplay (doc);
  743.     else
  744.         SetScrollMax (doc);
  745.  
  746.     TEUpdate (&r, hTE);
  747. }
  748.  
  749.  
  750. /* ---------------------------------------------------------------- */
  751. /*                        Window Handler Routines                        */
  752. /* ---------------------------------------------------------------- */
  753.  
  754.  
  755. /*
  756.  * Handle mouse clicks in window.  The viewRect is never tested
  757.  * directly, because if it were, clicks along the top, left and
  758.  * bottom edges of the window wouldn't register.
  759.  */
  760.  
  761. static pascal void
  762. Mouse (Point thePt, long t, short mods)
  763. {
  764. WindowPtr    w;
  765. DocHandle    doc;
  766. ControlHandle    scroll;
  767. short    thePart;
  768. short    oldCtlValue;
  769.  
  770.     GetPort (&w);
  771.     doc = WindDocHandle (w);
  772.     scroll = DocScroll (doc);
  773.  
  774.     if ((thePart = TestControl (scroll, thePt)) == inThumb)
  775.     {
  776.         oldCtlValue = GetCtlValue (scroll);
  777.         if (TrackControl (scroll, thePt, nil) == inThumb)
  778.             ScrollText (doc, GetCtlValue (scroll) - oldCtlValue);
  779.     }
  780.     else if (thePart != 0)
  781.     {
  782.         scrollDoc = doc;        /* set globals for TrackScroll */
  783.         scrollPart = thePart;
  784.         (void) TrackControl (scroll, thePt, &TrackScroll);
  785.     }
  786.     else if (PtInText (w, thePt))
  787.     {
  788.         scrollDoc = doc;        /* set global for AutoScroll */
  789.         TEClick (thePt, (mods & shiftKey) != 0, DocTE (doc));
  790.     }
  791.  
  792.     SetScrollMax (doc);
  793. }
  794.  
  795.  
  796. /*
  797.  * Handle key clicks in window
  798.  */
  799.  
  800. static pascal void
  801. Key (short c, short code, short mods)
  802. {
  803. WindowPtr    w;
  804. DocHandle    doc;
  805.  
  806.     GetPort (&w);
  807.     doc = WindDocHandle (w);
  808.  
  809.     if (c != enter)
  810.         TEKey (c, DocTE (doc));
  811.     AdjustDisplay (doc);
  812.     DocDirty (doc) = true;
  813.     if (DocKeyProc (doc) != nil)    /* report event to the host */
  814.         (*DocKeyProc (doc)) ();
  815. }
  816.  
  817.  
  818. /*
  819.  * Update window.  The update event might be in response to a
  820.  * window resizing.  If so, move and resize the scroll bar,
  821.  * and recalculate the text display.
  822.  *
  823.  * The ValidRect call is done because the HideControl adds the
  824.  * control bounds box to the update region - which would generate
  825.  * another update event!  Since everything is redrawn below anyway,
  826.  * the ValidRect is used to cancel the update.
  827.  */
  828.  
  829. static pascal void
  830. Update (Boolean resized)
  831. {
  832. WindowPtr    w;
  833. DocHandle    doc;
  834. ControlHandle    scroll;
  835. TEHandle    hTE;
  836. Rect    r;
  837.  
  838.     GetPort (&w);
  839.     doc = WindDocHandle (w);
  840.     scroll = DocScroll (doc);
  841.     hTE = DocTE (doc);
  842.  
  843.     if (resized)
  844.     {
  845.         r = w->portRect;
  846.         EraseRect (&r);
  847.         HideControl (scroll);
  848.         r = (**scroll).contrlRect;
  849.         ValidRect (&r);
  850.         CalcScrollRect (w, &r);
  851.         SizeControl (scroll, 16, r.bottom - r.top);
  852.         MoveControl (scroll, r.left, r.top);
  853.         OverhaulDisplay (doc, false, (**hTE).crOnly >= 0);
  854.         ShowControl (scroll);
  855.     }
  856.     else
  857.     {
  858.         OverhaulDisplay (doc, false, false);
  859.         if (WindowIsActive (w))
  860.             DrawControls (w);    /* redraw scroll bar */
  861.         else
  862.         {
  863.             /* draw outline of scroll, erase interior */
  864.             r = (**scroll).contrlRect;
  865.             FrameRect (&r);
  866.             InsetRect (&r, 1, 1);
  867.             EraseRect (&r);
  868.         }
  869.     }
  870.  
  871.     DrawGrowBox (w);
  872. }
  873.  
  874.  
  875. /*
  876.  * When the window comes active, highlight the scroll bar appropriately.
  877.  * When the window is deactivated, hide the scroll bar (this is drawn
  878.  * immediately rather than invalidating the rectangle and waiting for
  879.  * Update(), because that just seems too slow).
  880.  *
  881.  * Redraw the grow box in any case.  Set the cursor (FixCursor avoids
  882.  * changing it from an ibeam to an arrow back to an ibeam, in the case
  883.  * where one edit window is going inactive and another is coming
  884.  * active).
  885.  *
  886.  * Report the event to the host.
  887.  */
  888.  
  889. static pascal void
  890. Activate (Boolean active)
  891. {
  892. WindowPtr    w;
  893. DocHandle    doc;
  894. ControlHandle    scroll;
  895. TEHandle    hTE;
  896. RgnHandle    oldClip;
  897. Rect        r;
  898.  
  899.     GetPort (&w);
  900.     doc = WindDocHandle (w);
  901.     scroll = DocScroll (doc);
  902.     hTE = DocTE (doc);
  903.  
  904.     DrawGrowBox (w);
  905.     if (active)
  906.     {
  907.         TEActivate (hTE);
  908.         HiliteScroll (doc);
  909.         ShowControl (scroll);
  910.     }
  911.     else
  912.     {
  913.         TEDeactivate (hTE);
  914.         /* hide scroll but don't show it being hidden */
  915.         oldClip = NewRgn ();
  916.         GetClip (oldClip);
  917.         SetRect (&r, 0, 0, 0, 0);
  918.         ClipRect (&r);
  919.         HideControl (scroll);
  920.         SetClip (oldClip);
  921.         DisposeRgn (oldClip);
  922.         /* now erase inside of scroll (but not outline, to avoid flicker) */
  923.         r = (**scroll).contrlRect;        /* erase scroll */
  924.         InsetRect (&r, 1, 1);            /* but not outline */
  925.         EraseRect (&r);
  926.     }
  927.     FixCursor (iBeamCursor);
  928.     if (DocActivateProc (doc) != nil)    /* report event to the host */
  929.         (*DocActivateProc (doc)) (active);
  930. }
  931.  
  932.  
  933. /*
  934.  * Close box was clicked.  If user specified notify proc, call it.
  935.  * Otherwise do default close operation (ask about saving if dirty,
  936.  * etc.).
  937.  */
  938.  
  939. static pascal void
  940. Close (void)
  941. {
  942. WindowPtr    w;
  943. DocHandle    doc;
  944.  
  945.     GetPort (&w);
  946.     doc = WindDocHandle (w);
  947.  
  948.     if (DocCloseProc (doc) != nil)
  949.         (*DocCloseProc (doc)) ();
  950.     else
  951.         (void) EWindowClose (w);
  952. }
  953.  
  954.  
  955. /*
  956.  * Clobber an edit window.  This routine is written defensively on the
  957.  * assumption that not all pieces of a complete edit window are present.
  958.  * This allows it to be called by SkelRmveWind() during window creation
  959.  * attempts if allocations fail.
  960.  *
  961.  * The window's skelWPropEditWind property structure will be disposed
  962.  * of by TransSkel, but the data associated with it (returned by
  963.  * WindDocHandle()) must be disposed of here.
  964.  *
  965.  * At this point it's too late to back out if any changes have been
  966.  * made to the text.
  967.  */
  968.  
  969. static pascal void
  970. Clobber (void)
  971. {
  972. WindowPtr    w;
  973. DocHandle    doc;
  974. TEHandle    hTE;
  975.  
  976.     GetPort (&w);
  977.     doc = WindDocHandle (w);
  978.  
  979.     /*
  980.      * Toss document record and any pieces that exist
  981.      */
  982.     if (doc != (DocHandle) nil)
  983.     {
  984.         if ((hTE = DocTE (doc)) != (TEHandle) nil)
  985.             TEDispose (hTE);                        /* toss text record */
  986.         DisposeHandle ((Handle) doc);
  987.     }
  988.     DisposeWindow (w);            /* toss window (scroll bar, too) */
  989.     FixCursor (iBeamCursor);
  990. }
  991.  
  992.  
  993. /*
  994.  * Blink the caret and make sure the cursor's an i-beam when it's
  995.  * in the non-scrollbar part of the window.
  996.  */
  997.  
  998. static pascal void
  999. Idle (void)
  1000. {
  1001. WindowPtr    w;
  1002. DocHandle    doc;
  1003.  
  1004.     GetPort (&w);
  1005.     doc = WindDocHandle (w);
  1006.  
  1007.     TEIdle (DocTE (doc));            /* blink that caret! */
  1008.     FixCursor (iBeamCursor);
  1009. }
  1010.  
  1011.  
  1012. /* ---------------------------------------------------------------- */
  1013. /*                        Internal File Routines                        */
  1014. /* ---------------------------------------------------------------- */
  1015.  
  1016.  
  1017. /*
  1018.  * Save the contents of the edit window.  If there is no file bound
  1019.  * to the window, ask for a file name.  If askForFile is true, ask
  1020.  * for a name even if the window is currently bound to a file.  If
  1021.  * bindToFile is true, bind the window to the file written to (if
  1022.  * that's different than the currently bound file), and clear the
  1023.  * window's dirty flag.
  1024.  *
  1025.  * Return true if the file was written without error.  Return false
  1026.  * if (a) user was asked for name and clicked Cancel (b) there was
  1027.  * some error writing the file.  In the latter case, the window is
  1028.  * not bound to any new name given by user.
  1029.  *
  1030.  * Always returns false if the window isn't an edit window.  This
  1031.  * simplifies EWindowSave, EWindowSaveAs, EWindowSaveCopy.  (They
  1032.  * don't do the test.)
  1033.  */
  1034.  
  1035. static Boolean
  1036. SaveFile (WindowPtr w, Boolean askForFile, Boolean bindToFile)
  1037. {
  1038. DocHandle    doc;
  1039. TEHandle    hTE;
  1040. short    f;
  1041. FInfo    fndrInfo;    /* finder info */
  1042. SFReply    tmpFile;
  1043. Handle    hText;
  1044. long    count;
  1045. OSErr    result;
  1046. Boolean    haveNewFile = false;
  1047.  
  1048.     if (!IsEWindow (w))
  1049.         return (false);
  1050.  
  1051.     doc = WindDocHandle (w);
  1052.     hTE = DocTE (doc);
  1053.  
  1054.     tmpFile = DocFile (doc);                    /* initialize default name */
  1055.     if (DocBound (doc) == false || askForFile)
  1056.     {
  1057.         /*SFPPutFile (dlogWhere, "\pSave file as:", editFile.fName, nil, &tmpFile,
  1058.                 putDlgID, SkelDlogFilter (nil, false));*/
  1059.         SFPPutFile (dlogWhere, "\pSave file as:", tmpFile.fName, nil, &tmpFile,
  1060.                 putDlgID, SkelDlogFilter (nil, false));
  1061.         SkelRmveDlogFilter ();
  1062.         SkelDoUpdates ();
  1063.         if (!tmpFile.good)
  1064.             return (false);
  1065.         else
  1066.         {
  1067.             haveNewFile = true;
  1068.             if (GetFInfo (tmpFile.fName, tmpFile.vRefNum, &fndrInfo)
  1069.                                                     == noErr) /* exists */
  1070.             {
  1071.                 if (fndrInfo.fdType != 'TEXT')
  1072.                 {
  1073.                     ErrMesg ("\pNot a TEXT File");
  1074.                     return (false);
  1075.                 }
  1076.             }
  1077.             else    /* doesn't exist.  create it. */
  1078.             {
  1079.                 if (Create (tmpFile.fName, tmpFile.vRefNum,
  1080.                             creator, 'TEXT') != noErr)
  1081.                 {
  1082.                     ErrMesg ("\pCan't Create");
  1083.                     return (false);
  1084.                 }
  1085.             }
  1086.         }
  1087.     }
  1088.     
  1089.     if (FSOpen (tmpFile.fName, tmpFile.vRefNum, &f) != noErr)
  1090.         ErrMesg ("\pCan't Open");
  1091.     else
  1092.     {
  1093.         FixCursor (watchCursor);
  1094.         (void) SetFPos (f, fsFromStart, 0L);
  1095.         hText = (**hTE).hText;
  1096.         HLock (hText);
  1097.         count = (**hTE).teLength;
  1098.         result = FSWrite (f, &count, *hText);
  1099.         (void) GetFPos (f, &count);
  1100.         (void) SetEOF (f, count);
  1101.         (void) FSClose (f);
  1102.         (void) FlushVol (nil, tmpFile.vRefNum);
  1103.         HUnlock (hText);
  1104.         FixCursor (iBeamCursor);
  1105.         if (result == noErr)
  1106.         {
  1107.             if (bindToFile)
  1108.             {
  1109.                 DocDirty (doc) = false;
  1110.                 if (haveNewFile)    /* name different than current */
  1111.                 {
  1112.                     SetWTitle (w, tmpFile.fName);
  1113.                     DocBound (doc) = true;
  1114.                     DocFile (doc) = tmpFile;
  1115.                 }
  1116.             }
  1117.             return (true);
  1118.         }
  1119.         ErrMesg ("\pWrite error!");
  1120.     }
  1121.     return (false);
  1122. }
  1123.  
  1124.  
  1125. /*
  1126.  * Read file from disk.  Return value indicates whether or not the
  1127.  * file was read in successfully.
  1128.  */
  1129.  
  1130. static Boolean
  1131. ReadFile (DocHandle doc)
  1132. {
  1133. TEHandle    hTE;
  1134. SFReply    sfReply;
  1135. Boolean    result = false;
  1136. short    f;
  1137. long    len;
  1138. Handle    h;
  1139.  
  1140.     hTE = DocTE (doc);
  1141.     sfReply = DocFile (doc);
  1142.     FixCursor (watchCursor);
  1143.     if (FSOpen (sfReply.fName, sfReply.vRefNum, &f) != noErr)
  1144.         ErrMesg ("\pCouldn't open file");
  1145.     else
  1146.     {
  1147.         (void) GetEOF (f, &len);
  1148.         if (len >= maxFileSize)
  1149.             ErrMesg ("\pFile is too big");
  1150.         else
  1151.         {
  1152.             h = (Handle) TEGetText (hTE);
  1153.             SetHandleSize (h, len);
  1154.             HLock (h);
  1155.             (void) FSRead (f, &len, *h);
  1156.             HUnlock (h);
  1157.             (**hTE).teLength = len;
  1158.             TESetSelect (0L, 0L, hTE);    /* set caret at start */
  1159.             DocDirty (doc) = false;
  1160.             result = true;
  1161.         }
  1162.         (void) FSClose (f);
  1163.     }
  1164.     FixCursor (iBeamCursor);
  1165.     return (result);
  1166. }
  1167.  
  1168.  
  1169. /* ------------------------------------------------------------ */
  1170. /*            Interface (Public) Lowest-level Routines            */
  1171. /* ------------------------------------------------------------ */
  1172.  
  1173.  
  1174. /*
  1175.  * Return true/false to indicate whether the window is really an
  1176.  * edit window.
  1177.  */
  1178.  
  1179. pascal Boolean
  1180. IsEWindow (WindowPtr w)
  1181. {
  1182.     return ((Boolean) (WindDocHandle (w) != nil));
  1183. }
  1184.  
  1185.  
  1186. /*
  1187.  * Return true/false to indicate whether the text associated with
  1188.  * the window has been changed since the last save/revert (or since
  1189.  * created, if not bound to file).
  1190.  */
  1191.  
  1192. pascal Boolean
  1193. IsEWindowDirty (WindowPtr w)
  1194. {
  1195. DocHandle    doc;
  1196.  
  1197.     if ((doc = WindDocHandle (w)) != nil)
  1198.         return (DocDirty (doc));
  1199.     return (false);
  1200. }
  1201.  
  1202.  
  1203. /*
  1204.  * Return a handle to the TextEdit record associated with the edit
  1205.  * window, or nil if it's not an edit window
  1206.  */
  1207.  
  1208. pascal TEHandle
  1209. GetEWindowTE (WindowPtr w)
  1210. {
  1211. DocHandle    doc;
  1212.  
  1213.     return ((doc = WindDocHandle (w)) == nil ? nil : DocTE (doc));
  1214. }
  1215.  
  1216.  
  1217. /*
  1218.  * Return true/false depending on whether or not the editor is
  1219.  * bound to a file, and a copy of the file info in the second
  1220.  * argument.  Pass nil for fileInfo if only want the return status.
  1221.  * Returns false if it's not an edit window.
  1222.  */
  1223.  
  1224. pascal Boolean
  1225. GetEWindowFile (WindowPtr w, SFReply *fileInfo)
  1226. {
  1227. DocHandle    doc;
  1228.  
  1229.     if ((doc = WindDocHandle (w)) != nil)
  1230.     {
  1231.         if (fileInfo != nil)
  1232.             *fileInfo = DocFile (doc);
  1233.         return (DocBound (doc));
  1234.     }
  1235.     return (false);
  1236. }
  1237.  
  1238.  
  1239. /* ---------------------------------------------------------------- */
  1240. /*                    Interface Display Routines                        */
  1241. /* ---------------------------------------------------------------- */
  1242.  
  1243.  
  1244. /*
  1245.  * Install event notification procedures for an edit window.
  1246.  */
  1247.  
  1248. pascal void
  1249. SetEWindowProcs (WindowPtr w, TEditKeyProcPtr pKey,
  1250.                     TEditActivateProcPtr pActivate, TEditCloseProcPtr pClose)
  1251. {
  1252. DocHandle    doc;
  1253.  
  1254.     if (w == nil)            /* reset window creation defaults */
  1255.     {
  1256.         e_key = pKey;
  1257.         e_activate = pActivate;
  1258.         e_close = pClose;
  1259.     }
  1260.     else if ((doc = WindDocHandle (w)) != (DocHandle) nil)
  1261.     {
  1262.         DocKeyProc (doc) = pKey;
  1263.         DocActivateProc (doc) = pActivate;
  1264.         DocCloseProc (doc) = pClose;
  1265.     }
  1266. }
  1267.  
  1268.  
  1269. /*
  1270.  * Change the text display characteristics of an edit window
  1271.  * and redisplay it.
  1272.  *
  1273.  * Scroll to home position before overhauling, because although
  1274.  * the overhaul sets the viewRect to display an integral number
  1275.  * of lines, there's no guarantee that the destRect offset will
  1276.  * also be integral except at home position.  Clipping is set to
  1277.  * an empty rect so the scroll doesn't show.
  1278.  */
  1279.  
  1280. pascal void
  1281. SetEWindowStyle (WindowPtr w, short font,
  1282.                 short size, short wrap, short just)
  1283. {
  1284. DocHandle    doc;
  1285. GrafPtr        savePort;
  1286. FontInfo    f;
  1287. TEHandle    hTE;
  1288. Rect        r;
  1289. short        oldWrap;
  1290.  
  1291.     if (w == nil)            /* reset window creation defaults */
  1292.     {
  1293.         e_font = font;
  1294.         e_size = size;
  1295.         e_wrap = wrap;
  1296.         e_just = just;
  1297.         return;
  1298.     }
  1299.  
  1300.     if ((doc = WindDocHandle (w)) != (DocHandle) nil)
  1301.     {
  1302.         GetPort (&savePort);
  1303.         SetPort (w);
  1304.         hTE = DocTE (doc);
  1305.         GetPort (&savePort);
  1306.         ScrollToHome (hTE);
  1307.  
  1308.         oldWrap = (**hTE).crOnly;
  1309.         (**hTE).crOnly = wrap;    /* set word wrap */
  1310.         TESetJust (just, hTE);    /* set justification */
  1311.  
  1312.         TextFont (font);         /* set the font and point size */
  1313.         TextSize (size);        /* of text record */
  1314.         GetFontInfo (&f);
  1315.         (**hTE).lineHeight = f.ascent + f.descent + f.leading;
  1316.         (**hTE).fontAscent = f.ascent;
  1317.         (**hTE).txFont = font;
  1318.         (**hTE).txSize = size;
  1319.  
  1320.         OverhaulDisplay (doc, true, (oldWrap >= 0 || wrap >= 0));
  1321.  
  1322.         SetPort (savePort);
  1323.     }
  1324. }
  1325.  
  1326.  
  1327. /*
  1328.  * Redo display.  Does not save current port.  This is used by hosts
  1329.  * that mess with the text externally to TransEdit.  The arguments
  1330.  * determine whether the text is scrolled to show the line with the
  1331.  * caret, whether the lineStarts are recalculated, and whether or not
  1332.  * the text should be marked dirty.
  1333.  */
  1334.  
  1335. pascal void
  1336. EWindowOverhaul (WindowPtr w, Boolean showCaret,
  1337.                         Boolean recalc, Boolean dirty)
  1338. {
  1339. DocHandle    doc;
  1340.  
  1341.     if ((doc = WindDocHandle (w)) != (DocHandle) nil)
  1342.     {
  1343.         OverhaulDisplay (doc, showCaret, recalc);
  1344.         DrawControls (w);
  1345.         DocDirty (doc) = dirty;
  1346.     }
  1347. }
  1348.  
  1349.  
  1350. /* ---------------------------------------------------------------- */
  1351. /*                        Interface Menu Routine                        */
  1352. /* ---------------------------------------------------------------- */
  1353.  
  1354.  
  1355. /*
  1356.  * Do Edit menu selection.  This is only valid if an edit
  1357.  * window is frontmost.  Cut, Paste, and Clear cause the window
  1358.  * to be marked dirty.  Copy does not.  Undo is unimplemented.
  1359.  */
  1360.  
  1361. pascal void
  1362. EWindowEditOp (short item)
  1363. {
  1364. DocHandle    doc;
  1365. TEHandle    hTE;
  1366. Boolean        modified = false;
  1367.  
  1368.     if (SystemEdit (item - 1))
  1369.         return;
  1370.  
  1371.     if ((doc = WindDocHandle (FrontWindow ())) == (DocHandle) nil)
  1372.         return;                /* not an edit window */
  1373.  
  1374.     hTE = DocTE (doc);
  1375.  
  1376.     switch (item)
  1377.     {
  1378.     /*
  1379.      * cut selection, put in TE Scrap, clear clipboard and put
  1380.      * TE scrap in it
  1381.      */
  1382.     case cut:
  1383.         TECut (hTE);
  1384.         (void) ZeroScrap ();
  1385.         (void) TEToScrap ();
  1386.         modified = true;
  1387.         break;
  1388.  
  1389.     /*
  1390.      * copy selection to TE Scrap, clear clipboard and put
  1391.      * TE scrap in it
  1392.      */
  1393.     case copy:
  1394.         TECopy (hTE);
  1395.         (void) ZeroScrap ();
  1396.         (void) TEToScrap ();
  1397.         break;
  1398.  
  1399.     /*
  1400.      * get clipboard into TE scrap, put TE scrap into edit record
  1401.      */
  1402.     case paste:
  1403.         (void) TEFromScrap ();
  1404.         TEPaste (hTE);
  1405.         modified = true;
  1406.         break;
  1407.  
  1408.     /*
  1409.      * delete selection without putting into TE scrap or clipboard
  1410.      */
  1411.     case clear:
  1412.         (void) TEDelete (hTE);
  1413.         modified = true;
  1414.         break;
  1415.     }
  1416.  
  1417.     AdjustDisplay (doc);
  1418.     if (modified)
  1419.         DocDirty (doc) = true;
  1420. }
  1421.  
  1422.  
  1423. /* ---------------------------------------------------------------- */
  1424. /*                        Interface File Routines                        */
  1425. /* ---------------------------------------------------------------- */
  1426.  
  1427.  
  1428. /*
  1429.  * Set file creator for any files created by TransEdit
  1430.  */
  1431.  
  1432. pascal void
  1433. SetEWindowCreator (OSType creat)
  1434. {
  1435.     creator = creat;
  1436. }
  1437.  
  1438.  
  1439.  
  1440. /*
  1441.  * Save the contents of the given window
  1442.  */
  1443.  
  1444. pascal Boolean
  1445. EWindowSave (WindowPtr w)
  1446. {
  1447.     return (SaveFile (w,        /* window to save */
  1448.                       false,    /* don't ask for file if have one */
  1449.                       true));    /* bind to new file if one given */
  1450. }
  1451.  
  1452.  
  1453. /*
  1454.  * Save the contents of the given window under a new name
  1455.  * and bind to that name.
  1456.  */
  1457.  
  1458. pascal Boolean
  1459. EWindowSaveAs (WindowPtr w)
  1460. {
  1461.     return (SaveFile (w,        /* window to save */
  1462.                       true,        /* ask for file even if have one */
  1463.                       true));    /* bind to new file if one given */
  1464. }
  1465.  
  1466.  
  1467. /*
  1468.  * Save the contents of the given window under a new name, but
  1469.  * don't bind to the name.
  1470.  */
  1471.  
  1472. pascal Boolean
  1473. EWindowSaveCopy (WindowPtr w)
  1474. {
  1475.     return (SaveFile (w,        /* window to save */
  1476.                       true,        /* ask for file even if have one */
  1477.                       false));    /* don't bind to file */
  1478. }
  1479.  
  1480.  
  1481. /*
  1482.  * Close the window.  If it's dirty and is either bound to a file
  1483.  * or (if not bound) has some text in it, ask about saving it first,
  1484.  * giving user option of saving changes, tossing them, or
  1485.  * cancelling altogether.
  1486.  *
  1487.  * Return true if the file was saved and the window closed, false if
  1488.  * user cancelled or there was an error.
  1489.  */
  1490.  
  1491. pascal Boolean
  1492. EWindowClose (WindowPtr w)
  1493. {
  1494. DocHandle    doc;
  1495. TEHandle    hTE;
  1496. SFReply        sfReply;
  1497.  
  1498.     if ((doc = WindDocHandle (w)) == (DocHandle) nil)
  1499.         return (false);
  1500.     hTE = DocTE (doc);
  1501.     sfReply = DocFile (doc);
  1502.  
  1503.     if ( (DocBound (doc) || (**hTE).teLength > 0) && DocDirty (doc))
  1504.     {
  1505.         switch (FakeAlert ("\pSave changes to \322", sfReply.fName,
  1506.                 "\p\323?", "\p", 3, 1, 2,
  1507.                 "\pSave", "\pCancel", "\pDon\325t Save"))    /* ask whether to save */
  1508.         {
  1509.  
  1510.         case 1:
  1511.             if (SaveFile (w,    /* window to save */
  1512.                           false,    /* don't ask for name */
  1513.                           false)    /* don't bind to name */
  1514.                     == false)
  1515.                 return (false);    /* canceled or error - cancel Close */
  1516.             break;
  1517.  
  1518.         case 2:            /* cancel Close */
  1519.             return (false);
  1520.  
  1521.         case 3:            /* toss changes */
  1522.             break;
  1523.         }
  1524.     }
  1525.     SkelRmveWind (w);
  1526.     return (true);
  1527. }
  1528.  
  1529.  
  1530. /*
  1531.  * Revert to saved version of file on disk.  The window must be an edit
  1532.  * window, and must be bound to a file.  Returns false if one of these
  1533.  * conditions is not met, or if they are met but there was an error
  1534.  * reading the file.
  1535.  *
  1536.  * The window need not be dirty; if it is, the user is asked
  1537.  * whether to really revert.
  1538.  */
  1539.  
  1540. pascal Boolean
  1541. EWindowRevert (WindowPtr w)
  1542. {
  1543. DocHandle    doc;
  1544. TEHandle    hTE;
  1545. SFReply        sfReply;
  1546.  
  1547.     if ((doc = WindDocHandle (w)) == (DocHandle) nil)
  1548.         return (false);
  1549.     if (!DocBound (doc))
  1550.         return (false);        /* no file to revert to */
  1551.     if (DocDirty (doc))
  1552.     {
  1553.         sfReply = DocFile (doc);
  1554.         if (FakeAlert ("\p\322", sfReply.fName,
  1555.                 "\p\323 has been changed.  Really revert?",
  1556.                 "\p", 2, 1, 1, "\pCancel", "\pRevert", "\p") == 1)
  1557.             return (false);
  1558.     }
  1559.     if (ReadFile (doc) == false)
  1560.         return (false);
  1561.     ScrollToHome (DocTE (doc));
  1562.     OverhaulDisplay (doc, true, true);
  1563.     DrawControls (w);
  1564.     ValidRect (&w->portRect);
  1565.     return (true);
  1566. }
  1567.  
  1568.  
  1569. /* ---------------------------------------------------------------- */
  1570. /*            Interface Initialization/Termination Routines            */
  1571. /* ---------------------------------------------------------------- */
  1572.  
  1573.  
  1574. /*
  1575.  * Set up the SFReply record for a document.  Ask for a file and use
  1576.  * the file selected if bindToFile is true.  Otherwise use title for
  1577.  * the name if it's non-nil, or make the document untitled if title is
  1578.  * nil or the empty string.
  1579.  *
  1580.  * Copy the name into the file info structure even if the window is
  1581.  * unbound, because the Save operations expect to find it there as the
  1582.  * most likely name to use.
  1583.  */
  1584.  
  1585. static Boolean
  1586. SetupFile (StringPtr title, Boolean bindToFile, SFReply *sfReply)
  1587. {
  1588. Str255        s;
  1589. OSType        type = 'TEXT';
  1590.  
  1591.     sfReply->vRefNum = 0;
  1592.     if (bindToFile)
  1593.     {
  1594.         SFPGetFile (dlogWhere, "\p", nil, 1, &type, nil, sfReply,
  1595.                 getDlgID, SkelDlogFilter (nil, false));
  1596.         SkelRmveDlogFilter ();
  1597.         return (sfReply->good);
  1598.     }
  1599.     if (title == nil || title[0] == 0)
  1600.     {
  1601.         Untitled (s);
  1602.         title = s;
  1603.     }
  1604.     BlockMove (title, sfReply->fName, (long) (title[0] + 1));
  1605.     return (true);
  1606. }
  1607.  
  1608.  
  1609. /*
  1610.  * Create and initialize an edit window and the associated data
  1611.  * structures.  If the window and data cannot be allocated, destroy
  1612.  * the window and return nil.  Otherwise return the window.
  1613.  *
  1614.  * The caller should set and restore the port before and after calling
  1615.  * SetupDocWind().
  1616.  */
  1617.  
  1618. static WindowPtr
  1619. SetupDocWind (WindowPtr w, Boolean visible, Boolean bindToFile, SFReply *sfReply)
  1620. {
  1621. DocHandle    doc;
  1622. SkelWindPropHandle    prop;
  1623. ControlHandle    scroll;
  1624. TEHandle    hTE;
  1625. Rect        r;
  1626.  
  1627.     if (!SkelWindow (w,        /* the window */
  1628.                 Mouse,        /* mouse click handler */
  1629.                 Key,        /* key click handler */
  1630.                 Update,        /* window updating procedure */
  1631.                 Activate,    /* window activate/deactivate procedure */
  1632.                 Close,        /* window close procedure */
  1633.                 Clobber,    /* window disposal procedure */
  1634.                 Idle,        /* idle proc */
  1635.                 true))        /* idle only when frontmost */
  1636.     {
  1637.         DisposeWindow (w);
  1638.         return (nil);
  1639.     }
  1640.  
  1641.     /*
  1642.      * After this point SkelRmveWind() can be called to remove the window
  1643.      * if any allocations fail.
  1644.      */
  1645.  
  1646.     /*
  1647.      * Get new document record, attach to window property list.
  1648.      * Also make document record point to window.
  1649.      */
  1650.  
  1651.     if (!SkelAddWindProp (w, skelWPropEditWind, (long) 0L))
  1652.     {
  1653.         SkelRmveWind (w);
  1654.         return (nil);
  1655.     }
  1656.     doc = New (DocRecord);
  1657.     if (doc == (DocHandle) nil)
  1658.     {
  1659.         SkelRmveWind (w);
  1660.         return (nil);
  1661.     }
  1662.     prop = SkelGetWindProp (w, skelWPropEditWind);
  1663.     (**prop).skelWPropData = (long) doc;
  1664.     DocWind (doc) = w;
  1665.  
  1666.     /*
  1667.      * Build the scroll bar.  Make sure the borders overlap the
  1668.      * window frame and the frame of the grow box.
  1669.      */
  1670.  
  1671.     CalcScrollRect (w, &r);
  1672.     scroll = NewControl (w, &r, "\p", true, 0, 0, 0, scrollBarProc, 0L);
  1673.     DocScroll (doc) = scroll;
  1674.  
  1675.     /*
  1676.      * Create the TE record used for text display.  Use default
  1677.      * characteristics.
  1678.      */
  1679.  
  1680.     CalcEditRect (w, &r);
  1681.     hTE = TENew (&r, &r);
  1682.     DocTE (doc) = hTE;
  1683.     SetClikLoop (AutoScroll, hTE);            /* set autoscroll proc */
  1684.  
  1685.     if (scroll == (ControlHandle) nil || hTE == (TEHandle) nil)
  1686.     {
  1687.         SkelRmveWind (w);
  1688.         return (nil);
  1689.     }
  1690.  
  1691.     /*
  1692.      * Install default event notification procedures, font characteristics.
  1693.      */
  1694.  
  1695.     SetEWindowProcs (w, e_key, e_activate, e_close);
  1696.     SetEWindowStyle (w, e_font, e_size, e_wrap, e_just);
  1697.  
  1698.     DocFile (doc) = *sfReply;
  1699.     DocBound (doc) = bindToFile;
  1700.     if (bindToFile && !ReadFile (doc))
  1701.     {
  1702.         SkelRmveWind (w);
  1703.         return (nil);
  1704.     }
  1705.     DocDirty (doc) = false;
  1706.  
  1707.     /*
  1708.      * Show window if specified as visible, and return a pointer to it.
  1709.      */
  1710.  
  1711.     OverhaulDisplay (doc, true, true);
  1712.     if (visible)
  1713.         ShowWindow (w);
  1714.  
  1715.     return (w);
  1716. }
  1717.  
  1718.  
  1719. /*
  1720.  * Initialize the window and associated data structures.
  1721.  * Return window pointer or nil if some sort of error.
  1722.  *
  1723.  * Preserves the current port.  If the window is visible,
  1724.  * an activate event will follow, at which point the port
  1725.  * will be set to the window.
  1726.  *
  1727.  * If a file is to be solicited, ask for it before creating the
  1728.  * window to avoid the following problem:  If you create a window
  1729.  * to be frontmost but invisible, putting up the getfile dialog
  1730.  * after creating the window causes the window to go behind the
  1731.  * first visible window when the file dialog goes away.
  1732.  */
  1733.  
  1734. pascal WindowPtr
  1735. NewEWindow (Rect *bounds, StringPtr title, Boolean visible,
  1736.             WindowPtr behind, Boolean goAway,
  1737.             long refNum, Boolean bindToFile)
  1738. {
  1739. WindowPtr    w;
  1740. GrafPtr        savePort;
  1741. SFReply        sfReply;
  1742.  
  1743.     if (!SetupFile (title, bindToFile, &sfReply))    /* user cancelled */
  1744.         return (nil);
  1745.  
  1746.     if (SkelQuery (skelQHasColorQD))
  1747.     {
  1748.         w = NewCWindow (nil, bounds, sfReply.fName, false, documentProc + 8,
  1749.                                 behind, goAway, refNum);
  1750.     }
  1751.     else
  1752.     {
  1753.         w = NewWindow (nil, bounds, sfReply.fName, false, documentProc + 8,
  1754.                                 behind, goAway, refNum);
  1755.     }
  1756.  
  1757.     if (w != (WindowPtr) nil)
  1758.     {
  1759.         GetPort (&savePort);
  1760.         w = SetupDocWind (w, visible, bindToFile, &sfReply);    /* nil if allocation failed */
  1761.         SetPort (savePort);
  1762.     }
  1763.  
  1764.     return (w);
  1765. }
  1766.  
  1767.  
  1768. /*
  1769.  * GetNewEWindow() is the resource equivalent of NewEWindow().  It reads in the
  1770.  * 'WIND' template and yanks window creation values out of it rather than using
  1771.  * GetWindow() since the latter would create the window visible right away.
  1772.  * NewEWindow() doesn't show the window until after the file has been read in, if
  1773.  * a file is to be read.  procID value from resource is ignored.
  1774.  */
  1775.  
  1776. /* 'WIND' resource structure */
  1777.  
  1778. typedef struct WTmpl WTmpl, **WTHandle;
  1779.  
  1780. struct WTmpl
  1781. {
  1782.     Rect    bounds;
  1783.     short    procId;
  1784.     short    visible;
  1785.     short    goAway;
  1786.     long    refCon;
  1787.     Str255    title;
  1788. };
  1789.  
  1790.  
  1791. pascal WindowPtr
  1792. GetNewEWindow (short resourceNum, WindowPtr behind, Boolean bindToFile)
  1793. {
  1794. WTHandle    h;
  1795. WindowPtr    w;
  1796.  
  1797.     h = (WTHandle) GetResource ('WIND', resourceNum);
  1798.     if (h == (WTHandle) nil)
  1799.         return ((WindowPtr) nil);
  1800.     MoveHHi ((Handle) h);
  1801.     HLock ((Handle) h);
  1802.     w = NewEWindow (&((**h).bounds), (**h).title, (Boolean) ((**h).visible != 0),
  1803.                     behind, (Boolean) ((**h).goAway != 0), (**h).refCon, bindToFile);
  1804.     HUnlock ((Handle) h);
  1805.     return (w);
  1806. }
  1807.  
  1808.  
  1809. /*
  1810.  * Look through the list of windows, shutting down all the edit
  1811.  * windows.  If any window is dirty, ask user about saving it first.
  1812.  * If the user cancels on any such request, ClobberEWindows returns
  1813.  * false.  If all edit windows are shut down, return true.  It is
  1814.  * then safe for the host to exit.
  1815.  *
  1816.  * When a window *is* shut down, have to start looking through the
  1817.  * window list again, since w no longer points anywhere
  1818.  * meaningful.
  1819.  */
  1820.  
  1821. pascal Boolean
  1822. ClobberEWindows (void)
  1823. {
  1824. WindowPtr    w;
  1825.  
  1826.     for (;;)
  1827.     {
  1828.         for (w = FrontWindow ();
  1829.                 w != nil;
  1830.                     w = (WindowPtr) ((WindowPeek) w)->nextWindow)
  1831.         {
  1832.             if (IsEWindow (w))
  1833.                 break;
  1834.         }
  1835.         if (w == nil)
  1836.             return (true);        /* all edit windows are shut down */
  1837.  
  1838.         if (w != FrontWindow ())
  1839.         {
  1840.             SelectWindow (w);
  1841.             ShowWindow (w);
  1842.             EWindowOverhaul (w, false, false, IsEWindowDirty (w));
  1843.             SetPort (w);
  1844.             ValidRect (&w->portRect);
  1845.         }
  1846.  
  1847.         if (EWindowClose (w) == false)
  1848.             return (false);        /* cancel or error */
  1849.     }
  1850. }
  1851.